/**
 * 
 */
package spitfire.ksim.algorithm;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

/**
 * @author Adam
 *
 */
public class KNearestNeighbor implements LazyClassificationAlgorithm {

	private final int k;	// Parameter K in KNN algorithm
	private Double similarity;
	private PriorityQueue<Pair> prioQueue;
//	private FuzzyEntry entry;
	
	public KNearestNeighbor(int k) {
		if (k < 1) {
			throw new RuntimeException("K can't be < 1");
		}
		this.k = k;
		prioQueue = new PriorityQueue<Pair>(k + 1);
	}
	
	private void doClear() {
		similarity = null;
		prioQueue.clear();
	}
	
	/* (non-Javadoc)
	 * @see spitfire.ksim.algorithm.LazyClassificationAlgorithm#classifySD(java.util.List, java.util.List)
	 */
	@Override
	public String classifySD(List<Double> dataList, List<FuzzyEntry> entryList) {
		doClear();
		if (entryList.size() == 0) {
			return null;
		}
		// Find the K entries with highest score
		for (FuzzyEntry entry : entryList) {
			double sc = SimilarityAlgorithm.calculateScore(dataList, entry.getRule());
			System.out.println("Score to "+entry.getSd()+"|"+entry.getNodeId()+entry.getSensorType()+": "+sc);
			if (sc > 0) {
				prioQueue.offer(new Pair(sc, entry));
				if (prioQueue.size() > k) {
					prioQueue.poll();
				}
			}
		}
		
		// All similarity equals 0
		if (prioQueue.size() == 0) {
			similarity = 0D;
			return null;
		}
		
		// case: K = 1
		if (k == 1) {
			Pair pair = prioQueue.peek();
			similarity = pair.score;
			return pair.entry.getSd();
		}
		
		// case: K != 1
		// calculate votes and highest score for each class(SD)
		ArrayList<String> sdList = new ArrayList<String>();
		int[] votes = new int[k];	// vote count of the SD
		double[] scores = new double[k];	// highest score of the SD
		for (Pair pair : prioQueue) {
			String sd = pair.entry.getSd();
			double score = pair.score;
			int index = sdList.indexOf(sd);
			if (index == -1) {
				// not found
				votes[sdList.size()] = 1;
				scores[sdList.size()] = score;
				sdList.add(sd);
			} else {
				// found
				votes[index]++;
				if (score > scores[index]) {
					scores[index] = score;
				}
			}
		}
		// Find the SD with score by highest votes and then highest score
		int resultIdx = 0;
		for (int i = 1; i < sdList.size(); i++) {
			if (votes[i] > votes[resultIdx]) {
				resultIdx = i;
			} else if (votes[i] == votes[resultIdx]
			        && scores[i] > scores[resultIdx]) {
				resultIdx = i;
			}
		}
		similarity = scores[resultIdx];
		return sdList.get(resultIdx);
	}
	
	/* (non-Javadoc)
	 * @see spitfire.ksim.algorithm.LazyClassificationAlgorithm#getSimilarity()
	 */
	@Override
	public double getSimilarity() {
		if (similarity == null) {
			throw new RuntimeException("Invoke classifySD() first");
		}
		return similarity;
	}
}
